/*
 * @Author: huangwei
 * @Date: 2021-06-21 20:08:43
 * @LastEditors: 黄威
 * @LastEditTime: 2022-02-17 15:40:04
 * @Description: 动态加载功能的插件
 */

import { Component, Prop, Vue } from 'vue-property-decorator'
import { Error } from '@/log'

function extendPlugin(pluginInstance: any, vm: Vue) {
    // 要继承的方法
    const keys: string[] = (pluginInstance.constructor || {}).extends || []

    // 代理相关方法
    for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        const value = pluginInstance[key]

        // 私有方法不可继承
        if (
            key === 'constructor' ||
            key.startsWith('_') ||
            typeof value !== 'function'
        ) {
            continue
        }

        // 代理方法
        defineObj(vm, key, function (...rest: any[]) {
            return pluginInstance[key](...rest)
        })
    }
}

function getPrototypeChain(obj: any, returnArr: boolean = true) {
    const protoChain = [Object.getOwnPropertyNames(obj)]
    while ((obj = Object.getPrototypeOf(obj))) {
        if (obj !== Object.prototype) {
            protoChain.push(Object.getOwnPropertyNames(obj))
        }
    }
    if (returnArr) {
        return protoChain.flat()
    }
    return protoChain
}

function defineObj(obj: any, key: string, val: any) {
    const property = Object.getOwnPropertyDescriptor(obj, key)
    if (property && property.configurable === false) {
        return
    }
    const getter: any = property && property.get
    const setter: any = property && property.set

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            return getter ? getter.call(obj) : val
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val
            if (newVal === value || (newVal !== newVal && value !== value)) {
                return
            }

            newVal = getter ? setter.call(obj, newVal) : newVal
            val = newVal
        },
    })
}

// 默认插件的位置（这人的插件是二三维一体化的）
const DefalutPluginClazzes = require.context(
    '../components/BindMap/plugins/',
    false,
    /.ts$/
)
function loadPlugin(key: string, vm: PluginMixin) {
    return new Promise((resolve, reject) => {
        try {
            let PluginClazz
            try {
                PluginClazz = DefalutPluginClazzes('./' + key + '.ts').default
            } catch (error) {
                PluginClazz = vm.PluginClazzes('./' + key + '.ts').default
            }
            PluginClazz._isVue = true

            resolve(PluginClazz)
        } catch (error) {
            Error('加载插件失败', error)

            reject(error)
        }
    })
}

@Component
export default class PluginMixin extends Vue {
    readonly PluginClazzes!: any

    /**
     * 激活的插件，导入不同的插件可以给地图附加不同的功能
     * @type {'graphic' | 'graticules' | 'kilometergrid' | 'analysis' | 'plotting'}
     */
    @Prop({ type: Array, default: () => [] })
    plugins!: string[]

    /**
     * 插件构造参数
     * @type {{[key:string]:any}}
     */
    @Prop({ type: Object, default: () => ({}) })
    pluginOptions!: { [key: string]: any }

    _loadPlugins() {
        for (let i = 0; i < this.plugins.length; i++) {
            const key = this.plugins[i]

            // 加载指定的类
            loadPlugin(key, this).then((PluginClazz: any) => {
                // 构造类
                const name = key + 'Plugin'
                ;(this as any)[name] = new PluginClazz(
                    this,
                    this.pluginOptions[key]
                )
                // 扩展相关方法
                extendPlugin((this as any)[name], this)
            })
        }
    }
}
